import java.util.ArrayList;
import java.util.Properties;
import java.util.Random;

//Tabs = 4
//************************************************************
//	COPYRIGHT 2011 Bread Zeppelin - ALL RIGHTS RESERVED
//
//This file is the product of Bread Zeppelin and cannot be 
//reproduced, copied, or used in any shape or form without 
//the express written consent of Bread Zeppelin.
//************************************************************
//
//	$ID: Concurrent_Assg6
//
//	$Date: Feb 13, 2012
//
//	Reason:
//
//*************************************************************

/** $Author: Ben Hare, Zach Huenink, Philipp Wittmaack, Peter Maresca */
/** $Revision: 1 */

//Default Package


/**
 * The restaurant which contains all the restaurant objects and employees
 *
 */

public class Restaurant {

	Properties config = new Properties();
	String fileName = "config.properties";

	private static Restaurant instance = null;

	//private int maxWaitListSize = 15;

	private int numTables;
	private int numHostesses;	
	private int numWaiters;
	private int numCooks;
	private int numFoodRunners;
	private int simSpeed;

	private Menu menu;
	private TablePool tablePool;
	private WaitingList waitingList;
	private HostessExecutor hostessExecutor;
	private WaitressExecutor waitressExecutor;
	private CookExecutor cookExecutor;
	private FoodRunnerExecutor foodRunnerExecutor;
	private Random randGen = new Random(System.currentTimeMillis());

	private Object lock = new Object();


	//needs to read from a configuration file
	private Restaurant(){

		try{
			config.load((getClass().getResourceAsStream(fileName)));
			numTables = Integer.parseInt(config.getProperty("numTables"));
			numHostesses = Integer.parseInt(config.getProperty("numHostesses"));
			numWaiters = Integer.parseInt(config.getProperty("numWaiters"));
			numCooks = Integer.parseInt(config.getProperty("numCooks"));
			numFoodRunners = Integer.parseInt(config.getProperty("numFoodRunners"));
			simSpeed = Integer.parseInt(config.getProperty("simSpeed"));

			// Get the Menu from configuration...
			this.menu = new Menu( new ArrayList<String>(10) );

		}catch(Exception e){
			e.printStackTrace();
		}

		this.setNumTables(numTables);
		this.setNumHostesses(numHostesses);
		this.hostessExecutor = new HostessExecutor(numHostesses);
		this.waitressExecutor = new WaitressExecutor(numWaiters);
		this.cookExecutor = new CookExecutor(numCooks);
		this.foodRunnerExecutor = new FoodRunnerExecutor(numFoodRunners);
		this.tablePool = new TablePool();
		this.waitingList = new WaitingList();

		this.setUpTables();
	}

	/**
	 * Restaurant Singleton
	 * 
	 * @param numTables - the number of tables in the restaurant
	 * @param numHostesses - the number of hostesses working at the restaurant
	 * @return 
	 */
	public static Restaurant getInstance() {

		if(instance == null){
			instance = new Restaurant();
		}

		return instance;

	}

	/**
	 * Creates and adds tables to tablePool of varying sizes: 1, 4, 6.
	 */
	private void setUpTables() {
		for(int counter =0; counter < numTables; counter++){
			int tableSize = randGen.nextInt(3);

			if(tableSize < 1){
				tableSize = 1;
			}
			else if(tableSize < 2){
				tableSize = 4;
			}
			else{
				tableSize = 6;
			}

			Table table = new Table( tableSize );
			tablePool.returnTable(table);
		}

	}


	/**
	 * Returns the waiting list of customers
	 * @return waitingList - The customer waiting list
	 */
	public WaitingList getWaitingList(){
		return waitingList;
	}


	/**
	 * Returns the simulation speed as specified in the config file
	 * @return simSpeed = the speed of the simulation
	 */
	public int getSimSpeed(){
		return simSpeed;
	}

	/**
	 * Asks for a table in the tablePool returning null if there is none
	 * 
	 * @param requestedSize The size needed for the customerParty
	 * @return A table in the queue
	 */
	public Table requestTable(int requestedSize){

		return tablePool.getTable( requestedSize );
	}

	/**
	 * Returns a table into the queue
	 * 
	 * @param t - the table to be returned
	 */
	public void returnATable(Table t) {
		tablePool.returnTable(t);
	}

	/**
	 * Returns the hostessExecutor
	 * 
	 * @return hostessExecutor - the hostessExecutor
	 */
	public HostessExecutor getHostessExecutor() {
		return hostessExecutor;
	}

	/**
	 * Sets the hostessExecutor
	 * 
	 * @param hostessExecutor - the hostessExecutor
	 */
	public void setHostessExecutor(HostessExecutor hostessExecutor) {
		this.hostessExecutor = hostessExecutor;
	}

	/**
	 * Sets the number of tables in the restaurant
	 * 
	 * @param numTables
	 */
	public void setNumTables(int numTables) {
		this.numTables = numTables;
	}

	/**
	 * Sets the number of hostesses working
	 * 
	 * @param numHostesses the number of hostesses
	 */
	public void setNumHostesses(int numHostesses) {
		this.numHostesses = numHostesses;
	}

	/**
	 * Adds a customer to the restaurant
	 * 
	 * @param customer - the customer to be added
	 */
	public void addCustomer(final CustomerParty customer){

		AcceptCustomer_Task acceptTask = new AcceptCustomer_Task(customer);
		hostessExecutor.execute(acceptTask);

	}

	public TablePool getTablePool() {
		return tablePool;
	}

	public Menu getMenu() {
		return menu;
	}

	public WaitressExecutor getWaitressExecutor() {
		return waitressExecutor;
	}

	public FoodRunnerExecutor getFoodRunnerExecutor() {
		return foodRunnerExecutor;
	}

	public CookExecutor getCookExecutor() {
		return cookExecutor;
	}

	public boolean isClosed() {

		if ( hostessExecutor.getActiveCount() == 0 
				&& waitressExecutor.getActiveCount() == 0 
				&& cookExecutor.getActiveCount() == 0
				&& foodRunnerExecutor.getActiveCount() == 0) 
		{
			return true;
		}
		return false;
	}

}
